import { popTarget, pushTarget } from "./dep";

let wid = 0;
class Watcher {
  constructor(vm, exprOrFn, cb, options) {
    this.id = wid++;
    this.vm = vm;
    this.cb = cb; // vm.$watch 等的一些实例方法
    this.options = options; // 其他一些内部参数

    this.getter = exprOrFn; // 更新渲染函数(updateComponent)，将内部传过来的回调函数 放到getter属性上

    this.depsId = new Set(); // 存放 dep 的 id
    this.deps = []; // 存放 dep
    this.get(); // 实现页面的渲染
  }

  get() {
    /*
   执行页面更新，就是执行方法 updateComponent, 本质还是调用 render 函数
   const updateComponent = () => {
     const vnode = vm._render(); // 调用 render 方法返回的虚拟 DOM
     vm._update(vnode); // 将虚拟 DOM 转换成真实 DOM
   }
   */

    // console.log('get')
    // 依赖收集
    pushTarget(this); // 存放 Watcher, 即挂载到 Dep.target 上
    this.getter(); // 执行更新渲染函数（updateComponent），本质是 render 函数
    popTarget(); // 删除 Watcher
  }

  /**
   * 存储对应的 Dep
   * @param {*} dep Dep 实例
   */
  addDep(dep) {
    const id = dep.id;
    // Watcher 中不能有重复的 dep，反之 dep 中也不能有重复的 Watcher
    if (!this.depsId.has(id)) {
      this.depsId.add(id);
      this.deps.push(dep);
      dep.addWatcher(this); // 调用 Dep 的 addWatcher 函数，添加对应的渲染 Watcher， 让 Dep 记住当前的 Watcher
    }
  }

  // 调用视图更新函数（updateComponent），本质是 render 函数
  update() {
    // console.log(this.id)
    this.get(); // 这种方式会对同一个id有多次的页面更新，不友好

    /**
      解决调用 this.get() 直接更新导致多次渲染的问题, 异步更新，等数据都处理好之后再对视图更新
      vm.arr.push(34);
      vm.arr.push(565);
      vm.arr.push(37774);
      vm.arr.push(3211);
    */
    // TODO...

  }
}

/**
 * 让每个属性自己对应的渲染函数，如果属性发生变化就调用对应的渲染函数去更新视图
 * 一个 watcher 对应多个 dep，一个属性对应多个 watcher
 */

export default Watcher;